#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <inttypes.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 4096

// 连接到服务器
int connect_to_server() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket creation failed");
        return -1;
    }
    
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
        perror("invalid address");
        close(sock);
        return -1;
    }
    
    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connection failed");
        close(sock);
        return -1;
    }
    
    return sock;
}

// 序列化字符串（长度前缀法）
int serialize_string(const char* str, uint8_t* buffer, size_t buffer_size) {
    size_t len = strlen(str);
    if (len + sizeof(uint32_t) > buffer_size) {
        return -1; // 缓冲区太小
    }
    
    // 先写入字符串长度（大端序）
    uint32_t net_len = htonl((uint32_t)len);
    memcpy(buffer, &net_len, sizeof(net_len));
    
    // 再写入字符串内容
    memcpy(buffer + sizeof(net_len), str, len);
    
    return sizeof(net_len) + len;
}

// 发送文件块请求
int send_chunk_request(int sock, const char* filename, uint64_t offset, uint64_t chunk_size) {
    uint8_t buffer[BUFFER_SIZE];
    int offset_pos = 0;
    
    // 序列化文件名
    int filename_len = serialize_string(filename, buffer + offset_pos, BUFFER_SIZE - offset_pos);
    if (filename_len < 0) {
        printf("文件名太长\n");
        return -1;
    }
    offset_pos += filename_len;
    
    // 序列化偏移量（大端序）
    uint64_t net_offset = htobe64(offset);
    memcpy(buffer + offset_pos, &net_offset, sizeof(net_offset));
    offset_pos += sizeof(net_offset);
    
    // 序列化块大小（大端序）
    uint64_t net_chunk_size = htobe64(chunk_size);
    memcpy(buffer + offset_pos, &net_chunk_size, sizeof(net_chunk_size));
    offset_pos += sizeof(net_chunk_size);
    
    // 发送请求
    if (send(sock, buffer, offset_pos, 0) < 0) {
        perror("发送请求失败");
        return -1;
    }
    
    return 0;
}

// 接收服务器响应
int receive_chunk_response(int sock, uint64_t* offset, uint64_t* chunk_size, 
                          uint8_t* data, size_t data_size, size_t* data_received) {
    uint8_t buffer[BUFFER_SIZE];
    int total_received = 0;
    
    // 接收偏移量
    while (total_received < sizeof(uint64_t)) {
        int received = recv(sock, buffer + total_received, 
                           sizeof(uint64_t) - total_received, 0);
        if (received <= 0) {
            perror("接收偏移量失败");
            return -1;
        }
        total_received += received;
    }
    
    // 解析偏移量
    uint64_t net_offset;
    memcpy(&net_offset, buffer, sizeof(net_offset));
    *offset = be64toh(net_offset);
    
    // 接收块大小
    total_received = 0;
    while (total_received < sizeof(uint64_t)) {
        int received = recv(sock, (uint8_t*)chunk_size + total_received, 
                           sizeof(uint64_t) - total_received, 0);
        if (received <= 0) {
            perror("接收块大小失败");
            return -1;
        }
        total_received += received;
    }
    
    // 转换块大小字节序
    *chunk_size = be64toh(*chunk_size);
    
    // 接收数据块
    *data_received = 0;
    while (*data_received < *chunk_size && *data_received < data_size) {
        int to_receive = *chunk_size - *data_received;
        if (to_receive > BUFFER_SIZE) {
            to_receive = BUFFER_SIZE;
        }
        
        int received = recv(sock, data + *data_received, to_receive, 0);
        if (received <= 0) {
            perror("接收数据失败");
            return -1;
        }
        
        *data_received += received;
        
        // 显示进度
        int progress = (int)((double)(*data_received) / (*chunk_size) * 100);
        printf("\r接收进度: %d%%", progress);
        fflush(stdout);
    }
    
    printf("\n");
    return 0;
}

int main(int argc, char* argv[]) {
    if (argc != 4) {
        printf("用法: %s <文件名> <偏移量> <块大小>\n", argv[0]);
        return 1;
    }
    
    const char* filename = argv[1];
    uint64_t offset = strtoull(argv[2], NULL, 10);
    uint64_t chunk_size = strtoull(argv[3], NULL, 10);
    
    // 连接到服务器
    int sock = connect_to_server();
    if (sock < 0) {
        return 1;
    }
    
    printf("已连接到服务器\n");
    
    // 发送文件块请求
    if (send_chunk_request(sock, filename, offset, chunk_size) < 0) {
        close(sock);
        return 1;
    }
    
    printf("已发送文件块请求: %s (偏移: %" PRIu64 ", 大小: %" PRIu64 ")\n", 
           filename, offset, chunk_size);
    
    // 接收服务器响应
    uint64_t received_offset;
    uint64_t received_chunk_size;
    uint8_t data[BUFFER_SIZE * 10]; // 足够大的缓冲区
    size_t data_received;
    
    if (receive_chunk_response(sock, &received_offset, &received_chunk_size, 
                              data, sizeof(data), &data_received) < 0) {
        close(sock);
        return 1;
    }
    
    printf("文件块接收成功:\n");
    printf("  偏移量: %" PRIu64 "\n", received_offset);
    printf("  块大小: %" PRIu64 "\n", received_chunk_size);
    printf("  实际接收: %zu 字节\n", data_received);
    
    // 将接收到的数据保存到文件
    char output_filename[256];
    snprintf(output_filename, sizeof(output_filename), "%s.chunk_%" PRIu64, filename, offset);
    
    FILE* file = fopen(output_filename, "wb");
    if (file) {
        fwrite(data, 1, data_received, file);
        fclose(file);
        printf("数据已保存到: %s\n", output_filename);
    } else {
        perror("保存文件失败");
    }
    
    close(sock);
    return 0;
}